package lumis.portal.deployment;

import java.io.File;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import javax.persistence.EntityManager;

import org.w3c.dom.Document;
import org.w3c.dom.Node;

import lumis.portal.PortalException;
import lumis.portal.authentication.SessionConfig;
import lumis.portal.dao.DaoException;
import lumis.portal.manager.ManagerFactory;
import lumis.portal.transaction.ITransaction;
import lumis.portal.transaction.PortalTransactionFactory;
import lumis.portal.util.PortalUtilInternal;
import lumis.util.XmlUtil;
import lumis.util.log.ILogger;
import lumis.util.log.LoggerFactory;

/**
 * Helper that handles service register in a module. 
 *
 * @version $Revision: 13093 $ $Date: 2011-05-28 18:40:18 -0300 (Sat, 28 May 2011) $
 * @since 6.0.0
 */
class ServiceRegisterHelper
{
	private static ILogger logger = LoggerFactory.getLogger(ServiceRegisterHelper.class);
	
	/**
	 * Register the services in a module.
	 * @param module	the module.
	 * @throws Exception
	 */
	static void registerModuleServices(IModule module)
	{
		((Module) module).clearErrors();
		File moduleArchive = DeploymentUtil.getArchiveFile(module.getId(), module.getType());
		List<String> serviceDefinitionList = findServiceDefinitionPaths(module, moduleArchive);
		registerModuleServices(module, serviceDefinitionList);
		updateModule(module);
	}
	
	/**
	 * Unregister the services in a module.
	 * @param module	the module.
	 * @throws Exception
	 */
	static void unregisterModuleServices(IModule module)
	{
		((Module) module).clearErrors();
		File moduleArchive = DeploymentUtil.getArchiveFile(module.getId(), module.getType());
		List<String> serviceIdList = findServiceDefinitionIds(module, moduleArchive);		
		unregisterModuleServices(module, serviceIdList);
		updateModule(module);
	}

	/**
	 * Updates the module with the error information.
	 * @param module	the module.
	 */
	private static void updateModule(IModule module)
	{
		ITransaction transaction = PortalTransactionFactory.createTransaction();
		try
		{
			transaction.begin();
			
			EntityManager entityManager = ManagerFactory.getEntityManager();
			Module persistedModule = entityManager.getReference(Module.class, module.getId());
			
			persistedModule.setErrorMessage(module.getErrorMessage());
			persistedModule.setNumErrors(module.getNumErrors());
			
			transaction.commit();
		}
		catch (DaoException e)
		{
			logger.error("Error trying to save the module '" + module.getDisplayName() + "'.", e);
		}
		finally
		{
			try
			{
				transaction.dispose();
			}
			catch (DaoException e)
			{
			}
		}
	}
	
	/**
	 * Returns the list of service paths of the module. 
	 * @param module		the module.
	 * @param moduleArchive	the module archive.
	 * @return				the list of service paths of the given module. 
	 */
	static List<String> findServiceDefinitionPaths(IModule module, File moduleArchive)
	{
		List<String> serviceDefinitionList = new ArrayList<String>();
		try
		{
			JarFile jarFile = new JarFile(moduleArchive);
			try
			{
				Enumeration<JarEntry> jarEntries = jarFile.entries();
				
				while(jarEntries.hasMoreElements())
				{
					JarEntry entry = jarEntries.nextElement();
					if (entry.getName().endsWith("servicedefinition.xml"))
					{
						String servicePath = entry.getName().split("/servicedefinition.xml")[0];
						serviceDefinitionList.add(servicePath);
					}
				}
			}
			finally
			{
				jarFile.close();
			}
		}
		catch (Exception e)
		{
			logger.error("Error trying to read services from the module '" + module.getDisplayName() + "'.", e);
			((Module) module).addError("Error trying to read services from the module '" + module.getDisplayName() + "'.", e);
		}
		
		return serviceDefinitionList;
	}
	
	/**
	 * Returns the list of service ids in a module.
	 * @param module		the module.
	 * @param moduleArchive	the module archive.
	 * @return				a list containing the service id of the given module.
	 */
	static List<String> findServiceDefinitionIds(
							IModule module,
							File moduleArchive)
	{
		List<String> servicePathList = findServiceDefinitionPaths(module, moduleArchive);
		List<String> serviceIdList = new ArrayList<String>();
		
		for (String servicePath: servicePathList)
		{
			try
			{
				Document serviceConfig = PortalUtilInternal.getDefinitionDocument(servicePath + "/servicedefinition.xml");
				Node serviceXmlNode = XmlUtil.selectSingleNode("serviceDefinition/service", serviceConfig);
				String serviceId = XmlUtil.readAttributeString("id", serviceXmlNode);
				serviceIdList.add(serviceId);
			}
			catch (PortalException e)
			{
				logger.error("Error trying to read service id for the service '" + servicePath + "' from the module '" + module.getDisplayName() + "'.", e);
				((Module) module).addError("Error trying to read service id for the service '" + servicePath + "' from the module '" + module.getDisplayName() + "'.", e);
			}
		}
		
		return serviceIdList;
	}
		
	/**
	 * Register the services in a module.
	 * @param module					the module.
	 * @param serviceDefinitionList		the list of service definition paths.
	 */
	private static void registerModuleServices(
							IModule module,
							List<String> serviceDefinitionList)
	{
		for (String serviceDefinitionPath: serviceDefinitionList)
		{			
			try
			{
				ManagerFactory.getServiceManager().register(
						SessionConfig.getCurrentSessionConfig(),
						serviceDefinitionPath,
						PortalTransactionFactory.getCurrentTransaction());
			}
			catch (Exception e)
			{
				logger.error("Error trying to register the service '" + serviceDefinitionPath + "' from module '" + module.getDisplayName() + "'.", e);
				((Module) module).addError("Error trying to register the service '" + serviceDefinitionPath + "'.", e);
			}
		}
	}
	
	/**
	 * Unregister the services in a module.
	 * @param module			the module.
	 * @param serviceIdList		the list of ids in a module.
	 */
	private static void unregisterModuleServices(
							IModule module,
							List<String> serviceIdList)
	{
		for (String serviceId: serviceIdList)
		{
			try
			{
				ManagerFactory.getServiceManager().unregister(
						SessionConfig.getCurrentSessionConfig(),
						serviceId,
						PortalTransactionFactory.getCurrentTransaction());
			}
			catch (Exception e)
			{
				logger.error("Error trying to unregister the service '" + serviceId + "' from module '" + module.getDisplayName() + "'.", e);
				((Module) module).addError("Error trying to unregister the service '" + serviceId + "'.", e);
			}
		}
	}
}